/*-------------------------------------------------------------------------
 *
 * jsonb_op.c
 *	 专用于 jsonb 的特殊运算符，用于各种索引访问方法
 *
 * Copyright (c) 2014-2022, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/jsonb_op.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/jsonb.h"

Datum jsonb_exists(PG_FUNCTION_ARGS)
{
	Jsonb	   *fc_jb = PG_GETARG_JSONB_P(0);
	text	   *fc_key = PG_GETARG_TEXT_PP(1);
	JsonbValue	fc_kval;
	JsonbValue *fc_v = NULL;

	/*
	 * 我们只匹配对象键（自然总是字符串），或
	 * 数组中的字符串元素。特别是，我们不匹配非字符串
	 * 标量元素。键/元素的存在仅在顶层考虑。
	 * 不会发生递归。
	 */
	fc_kval.type = jbvString;
	fc_kval.val.string.val = VARDATA_ANY(fc_key);
	fc_kval.val.string.len = VARSIZE_ANY_EXHDR(fc_key);

	fc_v = findJsonbValueFromContainer(&fc_jb->root,
									JB_FOBJECT | JB_FARRAY,
									&fc_kval);

	PG_RETURN_BOOL(fc_v != NULL);
}

Datum jsonb_exists_any(PG_FUNCTION_ARGS)
{
	Jsonb	   *fc_jb = PG_GETARG_JSONB_P(0);
	ArrayType  *fc_keys = PG_GETARG_ARRAYTYPE_P(1);
	int			fc_i;
	Datum	   *fc_key_datums;
	bool	   *fc_key_nulls;
	int			fc_elem_count;

	deconstruct_array(fc_keys, TEXTOID, -1, false, TYPALIGN_INT,
					  &fc_key_datums, &fc_key_nulls, &fc_elem_count);

	for (fc_i = 0; fc_i < fc_elem_count; fc_i++)
	{
		JsonbValue	fc_strVal;

		if (fc_key_nulls[fc_i])
			continue;

		fc_strVal.type = jbvString;
		/* 我们依赖于数组元素未被压缩 */
		fc_strVal.val.string.val = VARDATA_ANY(fc_key_datums[fc_i]);
		fc_strVal.val.string.len = VARSIZE_ANY_EXHDR(fc_key_datums[fc_i]);

		if (findJsonbValueFromContainer(&fc_jb->root,
										JB_FOBJECT | JB_FARRAY,
										&fc_strVal) != NULL)
			PG_RETURN_BOOL(true);
	}

	PG_RETURN_BOOL(false);
}

Datum jsonb_exists_all(PG_FUNCTION_ARGS)
{
	Jsonb	   *fc_jb = PG_GETARG_JSONB_P(0);
	ArrayType  *fc_keys = PG_GETARG_ARRAYTYPE_P(1);
	int			fc_i;
	Datum	   *fc_key_datums;
	bool	   *fc_key_nulls;
	int			fc_elem_count;

	deconstruct_array(fc_keys, TEXTOID, -1, false, TYPALIGN_INT,
					  &fc_key_datums, &fc_key_nulls, &fc_elem_count);

	for (fc_i = 0; fc_i < fc_elem_count; fc_i++)
	{
		JsonbValue	fc_strVal;

		if (fc_key_nulls[fc_i])
			continue;

		fc_strVal.type = jbvString;
		/* 我们依赖于数组元素未被压缩 */
		fc_strVal.val.string.val = VARDATA_ANY(fc_key_datums[fc_i]);
		fc_strVal.val.string.len = VARSIZE_ANY_EXHDR(fc_key_datums[fc_i]);

		if (findJsonbValueFromContainer(&fc_jb->root,
										JB_FOBJECT | JB_FARRAY,
										&fc_strVal) == NULL)
			PG_RETURN_BOOL(false);
	}

	PG_RETURN_BOOL(true);
}

Datum jsonb_contains(PG_FUNCTION_ARGS)
{
	Jsonb	   *fc_val = PG_GETARG_JSONB_P(0);
	Jsonb	   *fc_tmpl = PG_GETARG_JSONB_P(1);

	JsonbIterator *fc_it1,
			   *fc_it2;

	if (JB_ROOT_IS_OBJECT(fc_val) != JB_ROOT_IS_OBJECT(fc_tmpl))
		PG_RETURN_BOOL(false);

	fc_it1 = JsonbIteratorInit(&fc_val->root);
	fc_it2 = JsonbIteratorInit(&fc_tmpl->root);

	PG_RETURN_BOOL(JsonbDeepContains(&fc_it1, &fc_it2));
}

Datum jsonb_contained(PG_FUNCTION_ARGS)
{
	/* "contains" 的换位运算符 */
	Jsonb	   *fc_tmpl = PG_GETARG_JSONB_P(0);
	Jsonb	   *fc_val = PG_GETARG_JSONB_P(1);

	JsonbIterator *fc_it1,
			   *fc_it2;

	if (JB_ROOT_IS_OBJECT(fc_val) != JB_ROOT_IS_OBJECT(fc_tmpl))
		PG_RETURN_BOOL(false);

	fc_it1 = JsonbIteratorInit(&fc_val->root);
	fc_it2 = JsonbIteratorInit(&fc_tmpl->root);

	PG_RETURN_BOOL(JsonbDeepContains(&fc_it1, &fc_it2));
}

Datum jsonb_ne(PG_FUNCTION_ARGS)
{
	Jsonb	   *fc_jba = PG_GETARG_JSONB_P(0);
	Jsonb	   *fc_jbb = PG_GETARG_JSONB_P(1);
	bool		fc_res;

	fc_res = (compareJsonbContainers(&fc_jba->root, &fc_jbb->root) != 0);

	PG_FREE_IF_COPY(fc_jba, 0);
	PG_FREE_IF_COPY(fc_jbb, 1);
	PG_RETURN_BOOL(fc_res);
}

/*
 * B-树操作符类运算符，支持函数
 */
Datum jsonb_lt(PG_FUNCTION_ARGS)
{
	Jsonb	   *fc_jba = PG_GETARG_JSONB_P(0);
	Jsonb	   *fc_jbb = PG_GETARG_JSONB_P(1);
	bool		fc_res;

	fc_res = (compareJsonbContainers(&fc_jba->root, &fc_jbb->root) < 0);

	PG_FREE_IF_COPY(fc_jba, 0);
	PG_FREE_IF_COPY(fc_jbb, 1);
	PG_RETURN_BOOL(fc_res);
}

Datum jsonb_gt(PG_FUNCTION_ARGS)
{
	Jsonb	   *fc_jba = PG_GETARG_JSONB_P(0);
	Jsonb	   *fc_jbb = PG_GETARG_JSONB_P(1);
	bool		fc_res;

	fc_res = (compareJsonbContainers(&fc_jba->root, &fc_jbb->root) > 0);

	PG_FREE_IF_COPY(fc_jba, 0);
	PG_FREE_IF_COPY(fc_jbb, 1);
	PG_RETURN_BOOL(fc_res);
}

Datum jsonb_le(PG_FUNCTION_ARGS)
{
	Jsonb	   *fc_jba = PG_GETARG_JSONB_P(0);
	Jsonb	   *fc_jbb = PG_GETARG_JSONB_P(1);
	bool		fc_res;

	fc_res = (compareJsonbContainers(&fc_jba->root, &fc_jbb->root) <= 0);

	PG_FREE_IF_COPY(fc_jba, 0);
	PG_FREE_IF_COPY(fc_jbb, 1);
	PG_RETURN_BOOL(fc_res);
}

Datum jsonb_ge(PG_FUNCTION_ARGS)
{
	Jsonb	   *fc_jba = PG_GETARG_JSONB_P(0);
	Jsonb	   *fc_jbb = PG_GETARG_JSONB_P(1);
	bool		fc_res;

	fc_res = (compareJsonbContainers(&fc_jba->root, &fc_jbb->root) >= 0);

	PG_FREE_IF_COPY(fc_jba, 0);
	PG_FREE_IF_COPY(fc_jbb, 1);
	PG_RETURN_BOOL(fc_res);
}

Datum jsonb_eq(PG_FUNCTION_ARGS)
{
	Jsonb	   *fc_jba = PG_GETARG_JSONB_P(0);
	Jsonb	   *fc_jbb = PG_GETARG_JSONB_P(1);
	bool		fc_res;

	fc_res = (compareJsonbContainers(&fc_jba->root, &fc_jbb->root) == 0);

	PG_FREE_IF_COPY(fc_jba, 0);
	PG_FREE_IF_COPY(fc_jbb, 1);
	PG_RETURN_BOOL(fc_res);
}

Datum jsonb_cmp(PG_FUNCTION_ARGS)
{
	Jsonb	   *fc_jba = PG_GETARG_JSONB_P(0);
	Jsonb	   *fc_jbb = PG_GETARG_JSONB_P(1);
	int			fc_res;

	fc_res = compareJsonbContainers(&fc_jba->root, &fc_jbb->root);

	PG_FREE_IF_COPY(fc_jba, 0);
	PG_FREE_IF_COPY(fc_jbb, 1);
	PG_RETURN_INT32(fc_res);
}

/*
 * 哈希操作符类 jsonb 哈希函数
 */
Datum jsonb_hash(PG_FUNCTION_ARGS)
{
	Jsonb	   *fc_jb = PG_GETARG_JSONB_P(0);
	JsonbIterator *fc_it;
	JsonbValue	fc_v;
	JsonbIteratorToken fc_r;
	uint32		fc_hash = 0;

	if (JB_ROOT_COUNT(fc_jb) == 0)
		PG_RETURN_INT32(0);

	fc_it = JsonbIteratorInit(&fc_jb->root);

	while ((fc_r = JsonbIteratorNext(&fc_it, &fc_v, false)) != WJB_DONE)
	{
		switch (fc_r)
		{
				/* 旋转留给 JsonbHashScalarValue() */
			case WJB_BEGIN_ARRAY:
				fc_hash ^= JB_FARRAY;
				break;
			case WJB_BEGIN_OBJECT:
				fc_hash ^= JB_FOBJECT;
				break;
			case WJB_KEY:
			case WJB_VALUE:
			case WJB_ELEM:
				JsonbHashScalarValue(&fc_v, &fc_hash);
				break;
			case WJB_END_ARRAY:
			case WJB_END_OBJECT:
				break;
			default:
				elog(ERROR, "invalid JsonbIteratorNext rc: %d", (int) fc_r);
		}
	}

	PG_FREE_IF_COPY(fc_jb, 0);
	PG_RETURN_INT32(fc_hash);
}

Datum jsonb_hash_extended(PG_FUNCTION_ARGS)
{
	Jsonb	   *fc_jb = PG_GETARG_JSONB_P(0);
	uint64		fc_seed = PG_GETARG_INT64(1);
	JsonbIterator *fc_it;
	JsonbValue	fc_v;
	JsonbIteratorToken fc_r;
	uint64		fc_hash = 0;

	if (JB_ROOT_COUNT(fc_jb) == 0)
		PG_RETURN_UINT64(fc_seed);

	fc_it = JsonbIteratorInit(&fc_jb->root);

	while ((fc_r = JsonbIteratorNext(&fc_it, &fc_v, false)) != WJB_DONE)
	{
		switch (fc_r)
		{
				/* 旋转留给 JsonbHashScalarValueExtended() */
			case WJB_BEGIN_ARRAY:
				fc_hash ^= ((uint64) JB_FARRAY) << 32 | JB_FARRAY;
				break;
			case WJB_BEGIN_OBJECT:
				fc_hash ^= ((uint64) JB_FOBJECT) << 32 | JB_FOBJECT;
				break;
			case WJB_KEY:
			case WJB_VALUE:
			case WJB_ELEM:
				JsonbHashScalarValueExtended(&fc_v, &fc_hash, fc_seed);
				break;
			case WJB_END_ARRAY:
			case WJB_END_OBJECT:
				break;
			default:
				elog(ERROR, "invalid JsonbIteratorNext rc: %d", (int) fc_r);
		}
	}

	PG_FREE_IF_COPY(fc_jb, 0);
	PG_RETURN_UINT64(fc_hash);
}
